/*

gcc pc.c -lpthread && while true; do ./a.out; sleep 1; done

*/
#include <assert.h>
#include <stdio.h>
#include <pthread.h>
#define LEN 4
#define TURN LEN*2

char buf[LEN];
int head, tail, convert;
pthread_mutex_t mutex;
pthread_cond_t empty1, empty2, full;

#define EMPTY1  (tail == convert)
#define EMPTY2  (convert == head)
#define FULL    ((head+1)%LEN == tail)
#define IN(c)   {   buf[head++] = (c);      \
                    head %= LEN;            }
char OUT()   
{
    char tmp = buf[tail++];
    tail %= LEN;          
    return tmp;    
}

char CONVERT()
{   
    buf[convert] -= 'a'-'A';
    char tmp = buf[convert++];
    convert %= LEN;                
    return tmp;
}

void* produce(void* arg)
{
    for (int i = 0; i < TURN; i++)
    {
        pthread_mutex_lock(&mutex);
        while (FULL)
            pthread_cond_wait(&full, &mutex);
        IN('a'+i);
        printf("%c\n", 'a'+i);
        pthread_cond_signal(&empty2);
        pthread_mutex_unlock(&mutex);
    }
    return NULL;
}

void* compute(void* arg)
{
    for (int i = 0; i < TURN; i++)
    {
        pthread_mutex_lock(&mutex);
        while (EMPTY2)
            pthread_cond_wait(&empty2, &mutex);
        printf("   %c:", buf[convert]);
        printf("%c\n", CONVERT());
        pthread_cond_signal(&empty1);
        pthread_mutex_unlock(&mutex);
    }
    return NULL;
}

void* consume(void* arg)
{
    for (int i = 0; i < TURN; i++)
    {
        pthread_mutex_lock(&mutex);
        while (EMPTY1)
            pthread_cond_wait(&empty1, &mutex);
        printf("        %c\n", OUT());
        pthread_cond_signal(&full);
        pthread_mutex_unlock(&mutex);
    }
    return NULL;
}

int main(int argc, char** argv)
{
    head = tail = convert = 0;

    pthread_mutex_init(&mutex, NULL);
    pthread_cond_init(&empty1, NULL);
    pthread_cond_init(&empty2, NULL);
    pthread_cond_init(&full, NULL);

    pthread_t pid[3];
    pthread_create(pid, NULL, produce, NULL);
    pthread_create(pid+1, NULL, compute, NULL);
    pthread_create(pid+2, NULL, consume, NULL);

    pthread_join(pid[0], NULL);
    pthread_join(pid[1], NULL);
    pthread_join(pid[2], NULL);

    printf("\n-----------------------------------------------\n\n");
}